package ModelRepresentations;

import java.util.ArrayList;

import updatedModels.Utils.Utils;

// TODO: perhaps add a name to the model aswell as it makes it easier to keep track of which model is which
public class Model implements Cloneable {

	private ArrayList<ModelObject> modelObjects;
	
	private ArrayList<Model> children;
	
	public Model(){
		modelObjects = new ArrayList<ModelObject>();
		children = new ArrayList<Model>();
	}

	public ArrayList<ModelObject> getModelObjects() {
		return modelObjects;
	}
	
	/**
	 * Get all model objects, including those from submodels
	 * @return
	 */
	public ArrayList<ModelObject> getAllModelObjects(){
		ArrayList<ModelObject> modelObjects = new ArrayList<ModelObject>();
		modelObjects.addAll(this.getModelObjects());
		for(Model subModel : this.children){
			modelObjects.addAll(subModel.getAllModelObjects());
		}
		return modelObjects;
	}
	
	
	/**
	 * Get all model objects, including those from submodels, excluding edges(arrows)
	 * @return
	 */
	public ArrayList<ModelObject> getAllModelObjectsExcludingEdges(){
		ArrayList<ModelObject> modelObjects = new ArrayList<ModelObject>();
		
		for(ModelObject mObject : this.modelObjects){
			if(mObject == null || mObject.getStencil() == null) continue;
			if(!((mObject.getStencil().equals("SequenceFlow") || mObject.getStencil().equals("\"SequenceFlow\"")) || 
					mObject.getStencil().equals("MessageFlow") || mObject.getStencil().equals("\"MessageFlow\"") ||
					mObject.getStencil().equals("Association_Undirected") ||
					mObject.getStencil().equals("\"Association_Undirected\""))){
				modelObjects.add(mObject);
			}
		}
		
		for(Model subModel : this.children){
			modelObjects.addAll(subModel.getAllModelObjectsExcludingEdges());
		}
		return modelObjects;
	}
	
	

	public void setModelObjects(ArrayList<ModelObject> modelObjects) {
		this.modelObjects = modelObjects;
	}
	
	public void addModelObject(ModelObject modelObject){
		this.modelObjects.add(modelObject);
	}

	public ArrayList<Model> getChildren() {
		return children;
	}

	public void setChildren(ArrayList<Model> children) {
		this.children = children;
	}
	
	public void addToChildren(Model child){
		children.add(child);
	}
	
	@Override
	public Object clone(){
		Model cloneModel = new Model();
		ArrayList<ModelObject> clonedModelObjects = cloneModel.modelObjects;
		ArrayList<Model> clonedChildren = cloneModel.children;
		
		
		for(ModelObject mO : modelObjects){
			clonedModelObjects.add(mO);
		}
		
		for(Model model : children){
			clonedChildren.add((Model)model.clone());
		}
		
		return cloneModel;
	}
	
	/**
	 * gets all the edges in this model represented as EdgeModelObject's
	 * 
	 * Algorithm:
	 * Get all the modelObjects, for all modelobjects that is not edges get their outgoing property,
	 * save the ModelObject from which one took the outgoing property, go to the ModelObject the property says,
	 * save the outgoing from that object and create an EdgeModelObject from it
	 * 
	 * @return ArrayList<EdgeModelObject> representing the edges
	 */
	public ArrayList<EdgeModelObject> getAllEdges(){
		ArrayList<ModelObject> allModelObjects = this.getAllModelObjects();
		ArrayList<EdgeModelObject> edges = new ArrayList<EdgeModelObject>();
		
		for(ModelObject modelObject : allModelObjects){
			if(!(modelObject.getStencil().equals("\"SequenceFlow\"") || 
					modelObject.getStencil().equals("\"MessageFlow\"") || 
					modelObject.getStencil().equals("\"Association_Undirected\""))){
				// get the outgoing and take the outgoing on that one and then that's one edge
				ArrayList<String> outgoing = modelObject.getOutgoing();
				for(String s1 : outgoing){
					ModelObject mo = this.getModelObjectFromArrayList(s1, allModelObjects);
					// assuming all mo is edges and that edges only got one outgoing
					if(mo.getStencil().equals("\"SequenceFlow\"") || mo.getStencil().equals("\"MessageFlow\"") || 
							mo.getStencil().equals("\"Association_Undirected\"")){
						ModelObject mo2 = this.getModelObjectFromArrayList(mo.getOutgoing().get(0), allModelObjects);
						edges.add(new EdgeModelObject(mo, modelObject, mo2));
					}
				}
			}
		}
		
		return edges;
	}
	
	/**
	 * returns the ModelObject with the specified resource id
	 * @param resourceID the id of the ModelObject to get
	 * @return the ModelObject, null if not found
	 */
	public ModelObject getModelObject(String resourceID){
		ArrayList<ModelObject> allModelObjects = getAllModelObjects();
		return getModelObjectFromArrayList(resourceID, allModelObjects);
	}
	
	/**
	 * gets the ModelObject with the resourceID from the ArrayList
	 * @param resourceID the resourceID to search for
	 * @param allModelObjects the ArrayList to search in for the resourceID
	 * @return the ModelObject if found, null otherwise
	 */
	private ModelObject getModelObjectFromArrayList(String resourceID, ArrayList<ModelObject> allModelObjects){
		for(ModelObject modelObject : allModelObjects){
			if(modelObject.getResourceID().equals(resourceID) ||
					(Utils.removeQuotes(modelObject.getResourceID()).equals(resourceID))){
				return modelObject;
			}
		}
		return null;
	}
	
}
